package com.cowin.inf.service.impl;

import com.alibaba.fastjson.JSON;
import com.cowin.base.exception.ServiceException;
import com.cowin.base.result.Result;
import com.cowin.inf.dao.KyVehicleQueueMapper;
import com.cowin.inf.model.KyVehicleQueue;
import com.cowin.inf.model.KyVehicleQueueLog;
import com.cowin.inf.model.linkedlist.NodeRetun;
import com.cowin.inf.model.linkedlist.SingleLinkedList;
import com.cowin.inf.model.linkedlist.VehicleNode;
import com.cowin.inf.model.vo.KyVehicleQueueVo;
import com.cowin.inf.model.vo.QueueVo;
import com.cowin.inf.model.vo.VehicleRouteVo;
import com.cowin.inf.service.KyBdcVehicleService;
import com.cowin.inf.service.KyVehicleQueueLogService;
import com.cowin.inf.service.KyVehicleQueueService;
import com.cowin.base.AbstractServiceImpl;
import com.cowin.sys.model.TmBasSortRule;
import com.cowin.sys.model.TmBasVehiclequeue;
import com.cowin.sys.service.TmBasSortRuleService;
import com.cowin.sys.service.TmBasVehiclequeueService;
import com.cowin.sys.service.TmOfmOrderService;
import com.cowin.sys.service.TmVhcVehicleMovementService;
import com.google.common.collect.ImmutableMap;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;


/**
 * Created by  on 2019/10/17.
 *
 * @author CodeGenerator
 */
@Slf4j
@Service
@Transactional(rollbackFor = RuntimeException.class)
public class KyVehicleQueueServiceImpl extends AbstractServiceImpl<KyVehicleQueue> implements KyVehicleQueueService {
    /**
     * 一次排序的最大车辆数
     */
    private static final int LIMIT_SIZE = 20;
    private static final String CREATE_TIME = "createTime";
    private static final String K2 = "K2";
    private static final String P1 = "P1";
    private static final String K11 = "K11";
    private static final String P10 = "P10";
    private static Integer tZcount = 1;
    private static Integer zZcount = 1;
    @Resource
    private KyVehicleQueueMapper kyVehicleQueueMapper;

    @Resource
    private TmBasSortRuleService tmBasSortRuleService;

    @Resource
    private TmOfmOrderService tmOfmOrderService;

    @Resource
    private TmBasVehiclequeueService tmBasVehiclequeueService;

    @Resource
    private KyVehicleQueueLogService kyVehicleQueueLogService;
    @Resource
    private TmVhcVehicleMovementService tmVhcVehicleMovementService;
    @Resource
    private KyBdcVehicleService kyBdcVehicleService;

    private final Map<String, String> sortRuleModuleMap = ImmutableMap.of("K2", "1", "P1", "0");
    private final Map<Integer, String> sortRuleTypeMap = ImmutableMap.of(0, "lotId", 1, "carColor", 2, "carType", 3, "createTime");

    @Override
    public QueueVo bdcQueue(QueueVo queueVo) {
        String queueType = queueVo.getType().trim();
        String ulocNo = null;
        if (K2.equals(queueType)) {
            ulocNo = "HZ11_20";
        } else if (P1.equals(queueType)) {
            ulocNo = "TZ11_20";
        }
        //TmBasSortRule SORT_RULE_MODE 排序规则类型
        String sortRuleModule = sortRuleModuleMap.get(queueType);
        if (sortRuleModule == null) {
            throw new ServiceException("只可获取涂装 总装队列 请查看参数");
        }
        List<TmBasSortRule> sortRuleList = tmBasSortRuleService.findSortRuleByModule(sortRuleModule);
        if (K2.equals(queueType) && CollectionUtils.isEmpty(sortRuleList)) {
            throw new ServiceException("涂装路由规则未配置or 未启用");
        }

        //队列保证vin 只有一个 BDC 是否去除在队列数据
        List<String> vinList = queueVo.getVins();
        log.info("接收BDC总数据数据{}", vinList);
        List<String> vinHasRecordByUlocId = tmVhcVehicleMovementService.getVinHasRecordByUlocId(vinList, ulocNo);
        List<String> reduce = vinList.stream().filter(item -> !vinHasRecordByUlocId.contains(item)).collect(Collectors.toList());
        if (reduce.size() > 0) {
            log.info("集合中未过焊装涂装20点数据{}", reduce);
            kyBdcVehicleService.batchUpdateVehicleStatus(reduce, "1");
            vinList.removeAll(reduce);
            log.info("去除未过20点数据剩余{}", vinList);
        }
        List<String> queueList = kyVehicleQueueMapper.selectAll().stream().map(KyVehicleQueue::getVin).collect(Collectors.toList());
        log.info("已在锁定对列数据{}", queueList);
        vinList.removeAll(queueList);
        log.info("去除已在锁定对列数据剩余{}", vinList);
        //查找优先队列
        List<String> real = tmBasVehiclequeueService.findAll().stream().sorted(Comparator.comparing(TmBasVehiclequeue::getTmBasVehiclequeueId)).map(TmBasVehiclequeue::getVin).collect(Collectors.toList());
        log.info("当前优先队列{}", real);
        // 筛选 BDC给到的车辆集合 与优先对列里的车辆信息 对比  筛选出符合 优先车辆对列的车
        real.retainAll(vinList);
        //查询BDC对列配置最大值 没有 则使用 LIMIT_SIZE
        String bdcScopeNum = kyVehicleQueueMapper.findBDCScopeNum("BDC_Scope_Num" + queueVo.getType());
        log.info("队列阀值大小{}", bdcScopeNum);
        Integer scopeNum = LIMIT_SIZE;
        if (bdcScopeNum != null & !"".equals(bdcScopeNum)) {
            scopeNum = Integer.parseInt(bdcScopeNum);
        }
        // 入涂进行判断
        if (K2.equals(queueType)) {
            //当删选出来的优先对列 大于LIMIT_SIZE 直接返回优先对列
            if (real.size() >= scopeNum) {
                queueVo.setVins(real.subList(0, scopeNum));
                return queueVo;
            }
        }
        log.info("此次加入排序的优先队列{}", real);
        // 删除 BDC给到的车辆集合里包含优先对列的车
        int size = scopeNum - real.size() + 1;
        vinList.removeAll(real);
        log.info("移除优先队列剩余数据进行规则排序{}", vinList);
        // 判断总装是否有规则
        if (P1.equals(queueType) && CollectionUtils.isEmpty(sortRuleList)) {
            // 入总按照优先队列返回
            queueVo.setVins(real);
        } else if (vinList.size() > 0) {
            Map<String, Object> params = new HashMap<>(3);
            log.info("排序规则{}", JSON.toJSONString(sortRuleList));
            StringBuilder orderBy = new StringBuilder();
            orderBy.append("ORDER BY ");
            for (TmBasSortRule aSortRuleList : sortRuleList) {
                int key = aSortRuleList.getSortRuleType().intValue();
                orderBy.append(sortRuleTypeMap.get(key));
                orderBy.append(",");
            }
            if (!orderBy.toString().contains(CREATE_TIME)) {
                orderBy.append("createTime ASC");
            } else {
                orderBy.deleteCharAt(orderBy.length() - 1);
                orderBy.append(" ASC");
            }
            params.put("list", vinList);
            params.put("type", queueType);
            params.put("orderBy", orderBy);
            //查询排序队列vin 车辆此批颜色 。。。 属性
            List<VehicleRouteVo> vehicleImpressList = tmOfmOrderService.getVehicleImpressList(params);
            //规则第一优先级---最后优先级 排序 满足第一优先级的情况下 在筛选第二优先级
            for (int i = 0; i < sortRuleList.size(); i++) {
                int ruleType = sortRuleList.get(i).getSortRuleType().intValue();
                NodeRetun nodeRetun = formatQueue(vehicleImpressList, ruleType, size, real, i == sortRuleList.size() - 1);
                if (nodeRetun != null) {
                    size = nodeRetun.getSize();
                    real = nodeRetun.getReal();
                    if (real.size() >= vehicleImpressList.size()) {
                        //此次排出来数量已经够了  退出循环
                        log.info("数量已经够 退出");
                        break;
                    }
                    vehicleImpressList = nodeRetun.getList();
                }
            }
            if (real.size() > scopeNum) {
                queueVo.setVins(real.subList(0, scopeNum));
            } else {
                queueVo.setVins(real);
            }
        }

        log.info("异步插入排序结果---{}--- 到数据库", queueVo.getVins());
        CompletableFuture.runAsync(() -> {
            try {
                saveQueue(queueVo.getVins(), queueType);
            } catch (Exception e2) {
                e2.printStackTrace();
            }
        });
        return queueVo;
    }


    public void saveQueue(List<String> vins, String queueType) {
        log.info("锁定队列插入数据。。。");
        for (String vin : vins) {
            KyVehicleQueue kyVehicleQueue = new KyVehicleQueue();
            kyVehicleQueue.setQueueType(queueType);
            kyVehicleQueue.setVin(vin);
            kyVehicleQueue.setCreateTime(new Date());
            if (K2.equals(queueType)) {
                kyVehicleQueue.setSortCode(tZcount++);
                kyVehicleQueue.setWaitPoint(K11);
            } else if (P1.equals(queueType)) {
                kyVehicleQueue.setSortCode(zZcount++);
                kyVehicleQueue.setWaitPoint(P10);
            }
            super.save(kyVehicleQueue);
        }
    }

    private NodeRetun formatQueue(List<VehicleRouteVo> list, Integer type, Integer size, List<String> real, boolean isLast) {
        if (CollectionUtils.isEmpty(list)) {
            return null;
        }
        List<VehicleRouteVo> returnList = new ArrayList<>();
        SingleLinkedList singleLinkedList = new SingleLinkedList();
        //格式化为单链表结构
        VehicleNode singleLinkedNodeList = singleLinkedList.getSingleLinkedNodeList(list, type);
        VehicleNode temp = singleLinkedNodeList.next;
        while (true) {
            if (temp.next == null) {
                //没有下一个节点
                List<VehicleRouteVo> vehicleRouteVo = temp.getVehicleRouteVo();
                for (VehicleRouteVo aVehicleRouteVo : vehicleRouteVo) {
                    real.add(aVehicleRouteVo.getVin());
                }
                break;
            } else {
                //含有下一个节点 判断此次获得的vin码集合是否满足填满所需求的总队列长度
                if (temp.getVehicleRouteVo().size() >= size) {
                    returnList = temp.getVehicleRouteVo();
                    if (isLast) {
                        //判断是否是最后一次 是 则把vin码 加入到real 中
                        List<VehicleRouteVo> vehicleRouteVo = temp.getVehicleRouteVo();
                        for (VehicleRouteVo aVehicleRouteVo : vehicleRouteVo) {
                            real.add(aVehicleRouteVo.getVin());
                        }
                    } else {
                        break;
                    }
                } else {
                    returnList = temp.next.getVehicleRouteVo();
                    size = size - temp.getVehicleRouteVo().size();
                    List<VehicleRouteVo> vehicleRouteVo = temp.getVehicleRouteVo();
                    for (VehicleRouteVo aVehicleRouteVo : vehicleRouteVo) {
                        real.add(aVehicleRouteVo.getVin());
                    }
                }
            }
            temp = temp.next;
        }
        NodeRetun nodeRetun = new NodeRetun();
        nodeRetun.setList(returnList);
        nodeRetun.setSize(size);
        nodeRetun.setReal(real);
        return nodeRetun;
    }

    @Override
    public Integer bdcQueueBad(QueueVo queueVo) {
        List<String> vinList = queueVo.getVins();
        log.info("巷道损坏不能出车{}", vinList);
        List<KyVehicleQueue> kyVehicleQueues = kyVehicleQueueMapper.selectAll();
        log.info("队列查询{}", kyVehicleQueues);
        List<KyVehicleQueue> intersection = kyVehicleQueues.stream().filter(item -> vinList.contains(item.getVin())).collect(Collectors.toList());
        log.info("删选队列不能出车{}", intersection);
        int count = 0;
        for (KyVehicleQueue kyVehicleQueue : intersection) {
            KyVehicleQueueLog kyVehicleQueueLog = new KyVehicleQueueLog();
            BeanUtils.copyProperties(kyVehicleQueue, kyVehicleQueueLog);
            kyVehicleQueueLog.setQueueId(kyVehicleQueue.getId());
            kyVehicleQueueLog.setDeleteTime(new Date());
            kyVehicleQueueLog.setDeleteType("1");
            kyVehicleQueueLogService.save(kyVehicleQueueLog);
            int save = kyVehicleQueueMapper.deleteByPrimaryKey(kyVehicleQueue);
            count += save;
        }
        return count;
    }

    @Override
    public KyVehicleQueue findQueueFirst(String queueType, String waitPoint) {
        return kyVehicleQueueMapper.findQueueFirst(queueType, waitPoint);
    }

    @Override
    public KyVehicleQueueVo findIsInQueue(String queueType, String vin) {
        return kyVehicleQueueMapper.findIsInQueue(queueType, vin);
    }

    @Override
    @Async
    public void remove(String queueType, String vin) {
        KyVehicleQueueVo isInQueue = kyVehicleQueueMapper.findIsInQueue(queueType, vin);
        log.info("从{} 队列中移除 {} ", queueType, vin);
        int remove = kyVehicleQueueMapper.remove(queueType, vin);
        log.info("移除{}条", remove);
        if (remove == 1) {
            KyVehicleQueueLog kyVehicleQueueLog = new KyVehicleQueueLog();
            BeanUtils.copyProperties(isInQueue, kyVehicleQueueLog);
            kyVehicleQueueLog.setQueueId(isInQueue.getId());
            kyVehicleQueueLog.setDeleteTime(new Date());
            kyVehicleQueueLog.setDeleteType("0");
            kyVehicleQueueLogService.save(kyVehicleQueueLog);
        }
    }

    @Override
    @Async
    public void updateWaitPoint(String queueType, String vin, String waitPoint) {
        kyVehicleQueueMapper.updateWaitPoint(queueType, vin, waitPoint);
    }
}
